package java_thinking.unit_15;

import java.util.List;

/**
 * @Description :  当使用泛型类时，必须在创建对象时指定类型参数的值，而使用泛型方法时，
 *              通常不必指明参数类型，因为编译器会为我们找出具体类型，这成为类型参数推断，
 *
 *              在泛型代码内部，无法获得任何有关放行参数类型的信息，java泛型是使用擦除来实现的，详见java_thinking/unit_15/_15_7/LostInformation.java
 *
 *              1.  不能将基本类型作为类型参数，解决方法是用包装类，如果性能成为问题，就要使用专门适配基本类型的容器版本；
 *              2.  一个类不能实现同一个泛型接口的两种变体，由于擦除的原因，这两个变体会成为相同的接口,如：120 行
 * @author: cww
 * @DateTime: 2019/4/2 20:11
 */

public class T_Theory<T> {
    /**
     * static无法访问泛型类的类型参数，
     * @return
     */
//    public static List<T> getObject(){
// 编译无法通过这和我们在静态方法中引用一个非静态的方法报的错类似，原因也是类似的

    /**
     * 当传入基本类型，自动打包机制就会介入其中，将基本类型的值包装为对应的对象，
     * @return
     */
    public  List<T> getObject(){
        return null;
    }

}

/**
 * 所以，如果static要使用泛型能力，就必须成为泛型方法
 */
class T_1 {
    /**
     * T_1是不是泛型与下面这个静态方法无关
     * <T> T  中T是表示返回类型
     * @param c
     * @param <T>
     * @return
     */
    public static <T> T f(T c) {
        return c;
    }
}

class stack<E,U> {
    public static <T> T print(){
        //T表示返回类型
        return (T)new Object();
    }

    /**
     * 没有返回
     * @param <T>
     */
    public static <T> void print1(){
        System.out.println("no return");
    }

    /**
     * 注意这里的类型参数是T,F，与类的<E,U>不一样
     * @param <T>
     * @param <F>
     * @return
     */
    public static <T,F> F print2(){
        return (F)new Object();
    }

    /**
     * 下面这样就不行,编译无法通过，因为这个泛型方法参数列表中没有U这个参数
     * 静态声明成员不能使用类的类型参数，因为类的参数是不固定的，静态方法引用非静态方法
     * Cannot make a static reference to a non_static reference
     */
//    public static <T> U print3(){
//        return (U)new Object();
//    }

    // public static void fun5(M val) { //会出错误
    // 因为泛型是要在对象创建的时候才知道是什么类型的，
    // 而对象创建的代码执行先后顺序是static的部分，然后才是构造函数等等。
    // 所以在对象初始化之前static的部分已经执行了，
    // 如果你在静态部分引用了泛型，那么毫无疑问虚拟机根本不知道是什么东西，
    // 因为这个时候类还没有初始化。因此在静态方法、数据域或初始化语句中，
    // 为了类而引用class F3<M>声明的泛型类型参数是非法的
    // }

    /**
     * 要明确一点，泛型作用是确定具体类型。先看一个泛型方法，使用了泛型参数T作为返回值，
     * 当使用对象时来调用该方法时，T类型会变成具体类型。第二个泛型方法是静态的，使用了T作为返回值和方法参数类型，
     * 但是静态方法是属于类的，类直接调用的话，T类型无法指定具体类型，那么该方法就没有意义。所以直接报错。第三个也是静态方法，
     * 但是该静态方法是自定义一个泛型参数，并非使用类型参数。所以当传入一个具体类型时，该静态方法的<W>就是具体类型了。
     * 两者静态方法的区别就是一个是使用泛型参数，一个是定义泛型方法。

     */

    /**
     * 这样写Hourly是不能编译的因为擦除会将Payable<Employee>和Payable<Hourly>简化为相同的类Payable
     * 但是如果将泛型参数移除掉就可以编译了
     * @param <T>
     */
    interface Payable<T> {}
    class Employee implements Payable<Employee> {}
    /**
     * 这样无法通过编译
     */
//    class Hourly extends Employee implements Payable<Hourly> {}

    /**
     * 这样可以
     */
    class Employee1 implements Payable<Employee1>{}
}

